package com.Angaliwa
{
	import mx.containers.Canvas;
	
	public class Layers
	{
		public var parentObject:*;
		public var resizeHandler:ResizeHandler = Core.g('resizeHandler');
		public var layerNameStart:String = "LAYER_";
		
		/* -- These are the suggested methods, so they might not make it to the cut. -- */
		
		// There are three main layer levels; highest, lowest and normal. Each layer will be
		// categorized in each of these levels, which will make it easier to process them.
		// {name: '(String) Layer Name', level: '(int) Layer level', obj: '(*) The layer object'}
		//private var highest:Array,lowest:Array,normal:Array = [];
		private var levels:Array  = ['highest','normal','lowest'];
		public var layers:Object = [];
		
		public function Layers(parent:*){
			parentObject = parent;
			
			levels.forEach(function(n:String, i:int, a:Array):void{
				layers[n] = new Array();
			});
		}
		
		/* 
			-- Short explanation of the 'level' variable found in some of the methods --
			
			The level variable should not be seen as the literal level (int). the level
			variable is smart, in that it accepts both numbers, as-well as certain words.
			
			Examples of accepted commands:
				highest, lowest, normal
			
			In addition, the aforementioned commands can be used in combination with an 
			additional level (int), for example:
				highest+50
				highest-10
			
			This allows you to specify which object is more or less important than the
			other (possible) objects with the same level (highest or lowest). This can 
			come in very handy when working with UI elements, for example. A UserDock
			is more important than the whiteboard, but a toolbar is even more important
			than an UserDock.
			
			You can also use a previously created layer name, giving you the freedom to
			'stick' multiple layers together. For example, the layers 'main' and 'UI'
			are created, and a new layer called 'whiteboard' is created, which is related
			to 'main'. You can then specify:
				main+1, or;
				main-1, or just;
				main
			
			When only a layer name is provided (as the last example does), the layer is
			not created, it will be more like a symbolic link to the specified layer.
			
			Note: + is always HIGHER, - is always LOWER. So lowest+2 will be on top of
				  lowest-5, not the other way around.
		*/
		
		// Creates a new layer
		// Name: Can be (string) for one layer or (array) for multiple layers. level
		//		 will be ignored when name is (array).
		// Returns: Layers (this)
		public function createLayer(name:String, level:String=undefined, colorizeBg:Boolean=false):Layers{
			var a:String,l:Number=0;
			
			var c:Canvas = new Canvas();
			c.verticalScrollPolicy = "off";
			c.horizontalScrollPolicy = "off";
			
			c.id,c.name = layerNameStart+name;
			c.styleName = layerNameStart+name;
			
			// Debug only, so we can clearly see each layer.
			if(colorizeBg){
				c.setStyle('backgroundColor', Toolbox.getRandomColor());
				c.setStyle('backgroundAlpha', 0.4);
			}
			
			if(level.indexOf("+") > 0){
				a = level.substring(0, level.indexOf("+"));
				l = Number(level.substr(level.indexOf("+")+1));
			} else if(level.indexOf("-") > 0){
				a = level.substring(0, level.indexOf("-"));
				l = Number(level.substr(level.indexOf("-")+1));
			} else a = level;
			
			if(layers[a] is Array){
				var start:int=0;
				
				if(l > 0 && l < layers[a].length)
					start = l;
				else if(l >= layers[a].length)
					start = (layers[a].length > 0) ? layers[a].length : 0;
				else 
					start = 0;
				
				var n:Object = {n:name,l:level,o:c};
				layers[a].push(n);
				
				if(layers[a].length>1){
					for(var i:int=layers[a].length-2; i >= start; i--)
						layers[a][i+1] = layers[a][i];
				}
				
				// Now inject the new layer into the right location
				layers[a][start] = n;
			}
			
			// Add the layer to the display.
			parentObject.addChild(c);
			
			c.x = 0;
			c.y = 0;
			
			//resizeHandler.a(c,'full',parentObject);
			resizeHandler.setSize(c, '100%', '100%');
			
			// Walk through all layers and make sure they are positioned correctly.
			refreshLayers();
			
			return this;
		}
		
		public function getLayerCount():Number{
			var totalCount:Number = 0;
			
			for(var c:String in layers){
				if(layers[c] is Array)
					totalCount += layers[c].length;
			}
			
			return totalCount;
		}
		
		public function getAllLayers():Array{
			var total:Array = new Array();
			
			for(var c:String in layers){
				for(var i:int=0; i < layers[c].length; i++)
					total.push(layers[c][i]);
			}
			
			return total;
		}
		
		public function refreshLayers():void{
			var total:Array = getAllLayers();
			var done:int = 1;
			
			for(var i:int=0; i < levels.length; i++){
				var crntLevel:String = levels[i];
			
				for(var l:int=0; l < layers[crntLevel].length; l++){
					parentObject.setChildIndex(layers[crntLevel][l].o, 
						parentObject.getChildren().length-(l+done));
				}
				
				done += layers[crntLevel].length;
			}
		}
		
		// Considering creating a separate method to process the level variable.
		// The tricky part is that the level is not only for layers, but also objects
		// inside layers, but for now we'll just focus on the layers part.
		/*
		private function processLevel(object:*, level:String, arrayObj:Object):Number{
			// Since we'll be using the following variables a lot, I've opted to
			// keep the names as short as possible. For the record:
			// a = "Action" (highest, lowest, normal, or a specific layer name)
			// l = "Level" (number, optional)
			// r = "Return object"
			// r1= "Return object index" (for easy reference, the layer array's index
			//	   number of the returning object is stored in this variable)
			// t = "Total layer count" (Array-ready total layer count.)
			var l:Number=0,a:String,r:Object,rl:Number;
			var t:Number=(layers.length>0) ? layers.length-1 : layers.length;
			
			if(level.indexOf("+") > 0){
				a = level.substring(0, level.indexOf("+"));
				l = Number(level.substr(level.indexOf("+")+1));
			} else if(level.indexOf("-") > 0){
				a = level.substring(0, level.indexOf("-"));
				l = Number(level.substr(level.indexOf("-")+1));
			} else a = level;
			
			switch(a){
				case "highest":
					if(l>0 && 0+l <= t && 0+l >= 0){
						rl=0+l;
						r = layers[rl];
					} else if(l>0 && 0+l > t){
						rl=t+1;
						r = layers[rl];
					} else{
						rl=0;
						r = layers[rl];
					}
				break;
				case "lowest":
					if(l>0 && t-l <= t && t-l >= 0){
						trace(object);
						trace('works! '+(t-l)); 
						rl=t-l;
						r = layers[rl];
						
					} else if(l>0 && t-l < 0){
						trace('too much! 0');
						rl=0;
						r = layers[rl];
					} else{
						trace('default! '+t);
						rl= t;
						r = layers[rl];
					}
				break;
			}
			
			// TRACE CODE
			//trace('A: '+a);
			//trace('L: '+l);
			
			//if(r) trace('R: '+r.r);
			//	else trace('R: 0');
			
			if(layers.length>0){
				trace(' ');
				trace('---start---');
				layers.forEach(function(o:*, i:int, a:Array):void{
					trace(i+': '+o.n);
				});
				
				trace(' ');
				trace('---NEW---');
				trace(' ');
			}
			
			var tmp:Array = layers.splice(rl);
			layers[rl] = arrayObj;
			
			tmp.forEach(function(o:*,i:int,a:Array):void{
				layers.push(o);
			});
			//layers = layers.concat(tmp);
			
			if(layers.length>0){
				layers.forEach(function(o:*, i:int, a:Array):void{
					trace(i+': '+o.n);
				});
				trace('---end---');
				trace(' ');
			}
			
			// Needs work. Obviously.
			return (r) ? r.r : 0;
		}
		*/
		
		// Add an object to the specified layer.
		// Returns: Layers (this)
		public function a(object:*, layer:String):Layers{
			var l:Object = g(layer);
			if(l) l.addChild(object);
			
			return this;
		}
		
		// Get a layer
		// Returns: *
		public function g(name:String, returnAllInfo:Boolean=false):*{
			for(var c:String in layers){
				for(var i:int=0; i < layers[c].length; i++){
					if(layers[c][i].n == name) 
						return (returnAllInfo) ? layers[c][i] : layers[c][i].o;
				}
			}
			
			return undefined;
		}
		
		// Delete a layer
		// Returns: * (parentObject)
		public function d(name:String):*{
			return undefined;
		}
		
		// Moves a layer in the specified direction
		// In addition to the levels mentioned in the short explanation, with this
		// function you can also use the commands 'up' and 'down' to move one level
		// up or down, respectively.
		// Returns: * (parentObject)
		public function reorder(name:String, level:String):*{
			return undefined;
		}
		
		// Move an object from it's original layer to the specified layer.
		// Returns: * (parentObject)
		public function move(object:*, layerName:String):*{
			return undefined;
		}
	}
}